package ren.helloworld.utils;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import ren.helloworld.utils.constant.MemoryConstants;
import ren.helloworld.utils.constant.TimeConstants;

/**
 * <pre>
 *     author: Blankj
 *     blog  : http://blankj.com
 *     time  : 2016/08/13
 *     desc  : 转换相关工具类
 * </pre>
 */
public final class ConvertUtils {

	private ConvertUtils() {
		throw new UnsupportedOperationException("u can't instantiate me...");
	}

	private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

	/**
	 * byteArr转hexString
	 * <p>例如：</p>
	 * bytes2HexString(new byte[] { 0, (byte) 0xa8 }) returns 00A8
	 *
	 * @param bytes 字节数组
	 * @return 16进制大写字符串
	 */
	public static String bytes2HexString(final byte[] bytes) {
		if (bytes == null) return null;
		int len = bytes.length;
		if (len <= 0) return null;
		char[] ret = new char[len << 1];
		for (int i = 0, j = 0; i < len; i++) {
			ret[j++] = hexDigits[bytes[i] >>> 4 & 0x0f];
			ret[j++] = hexDigits[bytes[i] & 0x0f];
		}
		return new String(ret);
	}

    /**
     * hexString转byteArr
     * <p>例如：</p>
     * hexString2Bytes("00A8") returns { 0, (byte) 0xA8 }
     *
     * @param hexString 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexString2Bytes(String hexString) {
	    if (isSpace(hexString)) return null;
	    int len = hexString.length();
	    if (len % 2 != 0) {
		    hexString = "0" + hexString;
		    len = len + 1;
	    }
	    char[] hexBytes = hexString.toUpperCase().toCharArray();
	    byte[] ret = new byte[len >> 1];
	    for (int i = 0; i < len; i += 2) {
		    ret[i >> 1] = (byte) (hex2Dec(hexBytes[i]) << 4 | hex2Dec(hexBytes[i + 1]));
	    }
	    return ret;
    }

	/**
	 * hexChar转int
	 *
	 * @param hexChar hex单个字节
	 * @return 0..15
	 */
	private static int hex2Dec(final char hexChar) {
		if (hexChar >= '0' && hexChar <= '9') {
			return hexChar - '0';
		} else if (hexChar >= 'A' && hexChar <= 'F') {
			return hexChar - 'A' + 10;
		} else {
			throw new IllegalArgumentException();
		}
	}

	/**
	 * charArr转byteArr
	 *
	 * @param chars 字符数组
	 * @return 字节数组
	 */
	public static byte[] chars2Bytes(final char[] chars) {
		if (chars == null || chars.length <= 0) return null;
		int len = chars.length;
		byte[] bytes = new byte[len];
		for (int i = 0; i < len; i++) {
			bytes[i] = (byte) (chars[i]);
		}
		return bytes;
	}

	/**
	 * byteArr转charArr
	 *
	 * @param bytes 字节数组
	 * @return 字符数组
	 */
	public static char[] bytes2Chars(final byte[] bytes) {
		if (bytes == null) return null;
		int len = bytes.length;
		if (len <= 0) return null;
		char[] chars = new char[len];
		for (int i = 0; i < len; i++) {
			chars[i] = (char) (bytes[i] & 0xff);
		}
		return chars;
	}

	/**
	 * 以unit为单位的内存大小转字节数
	 *
	 * @param memorySize 大小
	 * @param unit       单位类型
	 *                   <ul>
	 *                   <li>{@link MemoryConstants#BYTE}: 字节</li>
	 *                   <li>{@link MemoryConstants#KB}  : 千字节</li>
	 *                   <li>{@link MemoryConstants#MB}  : 兆</li>
	 *                   <li>{@link MemoryConstants#GB}  : GB</li>
	 *                   </ul>
	 * @return 字节数
	 */
	public static long memorySize2Byte(final long memorySize, @MemoryConstants.Unit final int unit) {
		if (memorySize < 0) return -1;
		return memorySize * unit;
	}

	/**
	 * 字节数转以unit为单位的内存大小
	 *
	 * @param byteNum 字节数
	 * @param unit    单位类型
	 *                <ul>
	 *                <li>{@link MemoryConstants#BYTE}: 字节</li>
	 *                <li>{@link MemoryConstants#KB}  : 千字节</li>
	 *                <li>{@link MemoryConstants#MB}  : 兆</li>
	 *                <li>{@link MemoryConstants#GB}  : GB</li>
	 *                </ul>
	 * @return 以unit为单位的size
	 */
	public static double byte2MemorySize(final long byteNum, @MemoryConstants.Unit final int unit) {
		if (byteNum < 0) return -1;
		return (double) byteNum / unit;
	}

	/**
	 * 字节数转合适内存大小
	 * <p>保留3位小数</p>
	 *
	 * @param byteNum 字节数
	 * @return 合适内存大小
	 */
	@SuppressLint("DefaultLocale")
	public static String byte2FitMemorySize(final long byteNum) {
		if (byteNum < 0) {
			return "shouldn't be less than zero!";
		} else if (byteNum < MemoryConstants.KB) {
			return String.format("%.3fB", (double) byteNum + 0.0005);
		} else if (byteNum < MemoryConstants.MB) {
			return String.format("%.3fKB", (double) byteNum / MemoryConstants.KB + 0.0005);
		} else if (byteNum < MemoryConstants.GB) {
			return String.format("%.3fMB", (double) byteNum / MemoryConstants.MB + 0.0005);
		} else {
			return String.format("%.3fGB", (double) byteNum / MemoryConstants.GB + 0.0005);
		}
	}

	/**
	 * 以unit为单位的时间长度转毫秒时间戳
	 *
	 * @param timeSpan 毫秒时间戳
	 * @param unit     单位类型
	 *                 <ul>
	 *                 <li>{@link TimeConstants#MSEC}: 毫秒</li>
	 *                 <li>{@link TimeConstants#SEC }: 秒</li>
	 *                 <li>{@link TimeConstants#MIN }: 分</li>
	 *                 <li>{@link TimeConstants#HOUR}: 小时</li>
	 *                 <li>{@link TimeConstants#DAY }: 天</li>
	 *                 </ul>
	 * @return 毫秒时间戳
	 */
	public static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {
		return timeSpan * unit;
	}

	/**
	 * 毫秒时间戳转以unit为单位的时间长度
	 *
	 * @param millis 毫秒时间戳
	 * @param unit   单位类型
	 *               <ul>
	 *               <li>{@link TimeConstants#MSEC}: 毫秒</li>
	 *               <li>{@link TimeConstants#SEC }: 秒</li>
	 *               <li>{@link TimeConstants#MIN }: 分</li>
	 *               <li>{@link TimeConstants#HOUR}: 小时</li>
	 *               <li>{@link TimeConstants#DAY }: 天</li>
	 *               </ul>
	 * @return 以unit为单位的时间长度
	 */
	public static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {
		return millis / unit;
	}

	/**
	 * 毫秒时间戳转合适时间长度
	 *
	 * @param millis    毫秒时间戳
	 *                  <p>小于等于0，返回null</p>
	 * @param precision 精度
	 *                  <ul>
	 *                  <li>precision = 0，返回null</li>
	 *                  <li>precision = 1，返回天</li>
	 *                  <li>precision = 2，返回天和小时</li>
	 *                  <li>precision = 3，返回天、小时和分钟</li>
	 *                  <li>precision = 4，返回天、小时、分钟和秒</li>
	 *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>
	 *                  </ul>
	 * @return 合适时间长度
	 */
	@SuppressLint("DefaultLocale")
	public static String millis2FitTimeSpan(long millis, int precision) {
		if (millis <= 0 || precision <= 0) return null;
		StringBuilder sb = new StringBuilder();
		String[] units = {"天", "小时", "分钟", "秒", "毫秒"};
		int[] unitLen = {86400000, 3600000, 60000, 1000, 1};
		precision = Math.min(precision, 5);
		for (int i = 0; i < precision; i++) {
			if (millis >= unitLen[i]) {
				long mode = millis / unitLen[i];
				millis -= mode * unitLen[i];
				sb.append(mode).append(units[i]);
			}
		}
		return sb.toString();
	}

	/**
	 * bytes转bits
	 *
	 * @param bytes 字节数组
	 * @return bits
	 */
	public static String bytes2Bits(final byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for (byte aByte : bytes) {
			for (int j = 7; j >= 0; --j) {
				sb.append(((aByte >> j) & 0x01) == 0 ? '0' : '1');
			}
		}
		return sb.toString();
	}

	/**
	 * bits转bytes
	 *
	 * @param bits 二进制
	 * @return bytes
	 */
	public static byte[] bits2Bytes(String bits) {
		int lenMod = bits.length() % 8;
		int byteLen = bits.length() / 8;
		// 不是8的倍数前面补0
		if (lenMod != 0) {
			for (int i = lenMod; i < 8; i++) {
				bits = "0" + bits;
			}
			byteLen++;
		}
		byte[] bytes = new byte[byteLen];
		for (int i = 0; i < byteLen; ++i) {
			for (int j = 0; j < 8; ++j) {
				bytes[i] <<= 1;
				bytes[i] |= bits.charAt(i * 8 + j) - '0';
			}
		}
		return bytes;
	}

	/**
	 * inputStream转outputStream
	 *
	 * @param is 输入流
	 * @return outputStream子类
	 */
	public static ByteArrayOutputStream input2OutputStream(final InputStream is) {
		if (is == null) return null;
		try {
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			byte[] b = new byte[MemoryConstants.KB];
			int len;
			while ((len = is.read(b, 0, MemoryConstants.KB)) != -1) {
				os.write(b, 0, len);
			}
			return os;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} finally {
			CloseUtils.closeIO(is);
		}
	}

	/**
	 * outputStream转inputStream
	 *
	 * @param out 输出流
	 * @return inputStream子类
	 */
	public ByteArrayInputStream output2InputStream(final OutputStream out) {
		if (out == null) return null;
		return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray());
	}

	/**
	 * inputStream转byteArr
	 *
	 * @param is 输入流
	 * @return 字节数组
	 */
	public static byte[] inputStream2Bytes(final InputStream is) {
		if (is == null) return null;
		return input2OutputStream(is).toByteArray();
	}

	/**
	 * byteArr转inputStream
	 *
	 * @param bytes 字节数组
	 * @return 输入流
	 */
	public static InputStream bytes2InputStream(final byte[] bytes) {
		if (bytes == null || bytes.length <= 0) return null;
		return new ByteArrayInputStream(bytes);
	}

	/**
	 * outputStream转byteArr
	 *
	 * @param out 输出流
	 * @return 字节数组
	 */
	public static byte[] outputStream2Bytes(final OutputStream out) {
		if (out == null) return null;
		return ((ByteArrayOutputStream) out).toByteArray();
	}

	/**
	 * outputStream转byteArr
	 *
	 * @param bytes 字节数组
	 * @return 字节数组
	 */
	public static OutputStream bytes2OutputStream(final byte[] bytes) {
		if (bytes == null || bytes.length <= 0) return null;
		ByteArrayOutputStream os = null;
		try {
			os = new ByteArrayOutputStream();
			os.write(bytes);
			return os;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} finally {
			CloseUtils.closeIO(os);
		}
	}

	/**
	 * inputStream转string按编码
	 *
	 * @param is          输入流
	 * @param charsetName 编码格式
	 * @return 字符串
	 */
	public static String inputStream2String(final InputStream is, final String charsetName) {
		if (is == null || isSpace(charsetName)) return null;
		try {
			return new String(inputStream2Bytes(is), charsetName);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * string转inputStream按编码
	 *
	 * @param string      字符串
	 * @param charsetName 编码格式
	 * @return 输入流
	 */
	public static InputStream string2InputStream(final String string, final String charsetName) {
		if (string == null || isSpace(charsetName)) return null;
		try {
			return new ByteArrayInputStream(string.getBytes(charsetName));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * outputStream转string按编码
	 *
	 * @param out         输出流
	 * @param charsetName 编码格式
	 * @return 字符串
	 */
	public static String outputStream2String(final OutputStream out, final String charsetName) {
		if (out == null || isSpace(charsetName)) return null;
		try {
			return new String(outputStream2Bytes(out), charsetName);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * string转outputStream按编码
	 *
	 * @param string      字符串
	 * @param charsetName 编码格式
	 * @return 输入流
	 */
	public static OutputStream string2OutputStream(final String string, final String charsetName) {
		if (string == null || isSpace(charsetName)) return null;
		try {
			return bytes2OutputStream(string.getBytes(charsetName));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * bitmap转byteArr
	 *
	 * @param bitmap bitmap对象
	 * @param format 格式
	 * @return 字节数组
	 */
	public static byte[] bitmap2Bytes(final Bitmap bitmap, final Bitmap.CompressFormat format) {
		if (bitmap == null) return null;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bitmap.compress(format, 100, baos);
		return baos.toByteArray();
	}

	/**
	 * byteArr转bitmap
	 *
	 * @param bytes 字节数组
	 * @return bitmap
	 */
	public static Bitmap bytes2Bitmap(final byte[] bytes) {
		return (bytes == null || bytes.length == 0) ? null : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
	}

	/**
	 * drawable转bitmap
	 *
	 * @param drawable drawable对象
	 * @return bitmap
	 */
	public static Bitmap drawable2Bitmap(final Drawable drawable) {
		if (drawable instanceof BitmapDrawable) {
			BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
			if (bitmapDrawable.getBitmap() != null) {
				return bitmapDrawable.getBitmap();
			}
		}
		Bitmap bitmap;
		if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
			bitmap = Bitmap.createBitmap(1, 1,
					drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
		} else {
			bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
					drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
		}
		Canvas canvas = new Canvas(bitmap);
		drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
		drawable.draw(canvas);
		return bitmap;
	}

	/**
	 * bitmap转drawable
	 *
	 * @param bitmap bitmap对象
	 * @return drawable
	 */
	public static Drawable bitmap2Drawable(final Bitmap bitmap) {
		return bitmap == null ? null : new BitmapDrawable(Utils.getContext().getResources(), bitmap);
	}

	/**
	 * drawable转byteArr
	 *
	 * @param drawable drawable对象
	 * @param format   格式
	 * @return 字节数组
	 */
	public static byte[] drawable2Bytes(final Drawable drawable, final Bitmap.CompressFormat format) {
		return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable), format);
	}

	/**
	 * byteArr转drawable
	 *
	 * @param bytes 字节数组
	 * @return drawable
	 */
	public static Drawable bytes2Drawable(final byte[] bytes) {
		return bytes == null ? null : bitmap2Drawable(bytes2Bitmap(bytes));
	}

	/**
	 * view转Bitmap
	 *
	 * @param view 视图
	 * @return bitmap
	 */
	public static Bitmap view2Bitmap(final View view) {
		if (view == null) return null;
		Bitmap ret = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(ret);
		Drawable bgDrawable = view.getBackground();
		if (bgDrawable != null) {
			bgDrawable.draw(canvas);
		} else {
			canvas.drawColor(Color.WHITE);
		}
		view.draw(canvas);
		return ret;
	}

	/**
	 * dp转px
	 *
	 * @param dpValue dp值
	 * @return px值
	 */
	public static int dp2px(final float dpValue) {
		final float scale = Utils.getContext().getResources().getDisplayMetrics().density;
		return (int) (dpValue * scale + 0.5f);
	}

	/**
	 * px转dp
	 *
	 * @param pxValue px值
	 * @return dp值
	 */
	public static int px2dp(final float pxValue) {
		final float scale = Utils.getContext().getResources().getDisplayMetrics().density;
		return (int) (pxValue / scale + 0.5f);
	}

	/**
	 * sp转px
	 *
	 * @param spValue sp值
	 * @return px值
	 */
	public static int sp2px(final float spValue) {
		final float fontScale = Utils.getContext().getResources().getDisplayMetrics().scaledDensity;
		return (int) (spValue * fontScale + 0.5f);
	}

	/**
	 * px转sp
	 *
	 * @param pxValue px值
	 * @return sp值
	 */
	public static int px2sp(final float pxValue) {
		final float fontScale = Utils.getContext().getResources().getDisplayMetrics().scaledDensity;
		return (int) (pxValue / fontScale + 0.5f);
	}

	/**
	 * 判断字符串是否为null或全为空白字符
	 *
	 * @param s 待校验字符串
	 * @return {@code true}: null或全空白字符<br> {@code false}: 不为null且不全空白字符
	 */
	private static boolean isSpace(final String s) {
		if (s == null) return true;
		for (int i = 0, len = s.length(); i < len; ++i) {
			if (!Character.isWhitespace(s.charAt(i))) {
				return false;
			}
		}
		return true;
	}
}
